let logger = require("pomelo-logger").getLogger(__filename);
let DateUtil = require("../../../utils/dateUtil");
let Poker = require("../../../domain/poker/zhajinhua");
let util = require('util');
let BaseTable = require("../base/yuanbaoTable");
let pomelo = require("pomelo");

let Table = function () {
    BaseTable.call(this);
    this.maxRounds = 0;
    this.minSeeCardRound = 0;
    this.minCompareRound = 0;
    this.compareMultiplier = 0;

    this.lasWinnerUid = 0;
    this.dealerUid = 0;
    this.cardVisibilities = {};
    this.actorTime = 0;
    this.actorUid = 0;
    this.potBets = 0;
    this.minBets = 0;
};

util.inherits(Table, BaseTable);

const ACTION_INTERVAL = 15;

/*
 */
Table.prototype.tick = function () {
    BaseTable.prototype.tick.call(this);
    let actor = this.findPlayerByUid(this.actorUid);
    if (actor == null) {
        return;
    }
    if (this.playingFlag > 0 && actor.getAllInValue() > 0) {
        if (!this.hasEnoughBetChip(actor) || this.getNumOfRounds() > this.maxRounds) {
            this.autoCompare(actor);
        } else {
            this.bet(actor, this.minBets);
        }
    }
    if (this.playingFlag > 0 && this.actorTime !== 0) {
        if (this.getNumOfRounds() <= this.minSeeCardRound) {
            this.bet(actor, this.minBets);
        } else if (DateUtil.getIntervalSec(this.actorTime) > ACTION_INTERVAL) {
            this.fold(actor);
        }
    }
};

Table.prototype.hasEnoughBetChip = function (actor) {
    let amount = this.minBets;
    if (!actor.isBlind()) {
        amount = amount * 2;
    }
    return actor.getNetBalance() > amount;
};

Table.prototype.autoCompare = function (actor) {
    let target = null;
    for (let i = 0; i < this.players.length; i++) {
        let player = this.players[i];
        if (player != null && player.isAlive() && player.getUid() !== actor.getUid()) {
            target = player;
            break;
        }
    }
    if (target != null) {
        this.compare(actor, target);
    }
};

Table.prototype.start = function () {
    logger.info("zhajinhua start");
    BaseTable.prototype.start.call(this);
    let self = this;
    let fee = this.getFee();
    let readyPlayers = this.getPlayingPlayers();
    let result = Poker.dealCards(readyPlayers.length);
    let index = 0;
    readyPlayers.forEach(function (player) {
        player.start(result[index], self.multiplier, fee);
        index = index + 1;
        self.potBets += self.multiplier;
    });
    let params = {uids: [], amounts:[], params:[]};
    for (let i = 0; i < readyPlayers.length; i++) {
        let player = readyPlayers[i];
        params.uids.push(player.getUid());
        params.amounts.push(-fee);
        params.params.push("zhajinhua_start");
    }
    pomelo.app.rpc.account.tableRemote.changeDiamond(null, params, function (){});
    this.minBets = this.multiplier;
    let lastWinner = null;
    readyPlayers.forEach(function (player) {
        if (player.getUid() === self.lasWinnerUid) {
            lastWinner = player;
        }
    });
    if (lastWinner != null) {
        this.dealerUid = lastWinner.getUid();
    } else {
        this.lasWinnerUid = 0;
        this.dealerUid = readyPlayers[0].getUid();
    }
    this.actorUid = this.dealerUid;
    this.actorTime = DateUtil.nowMS();
    this.broadCast("onZhajinhuaStart", {
        "dealerUid": this.dealerUid,
        "actorUid": this.actorUid,
        "potBets": this.potBets,
        "minBets": this.minBets,
        "rounds": this.getNumOfRounds(),
        "players": readyPlayers.map(function (player) {
            return player.toJSON();
        })
    });
};

Table.prototype.toJSON = function () {
    let playersJSON = [];
    this.players.forEach(function (player) {
        if (player != null) {
            let playerJSON = player.toJSON();
            playersJSON.push(playerJSON);
        }
    });
    return {
        tid: this.tid,
        code: this.code,
        multiplier: this.multiplier,
        maxRounds: this.maxRounds,
        minSeeCardRound: this.minSeeCardRound,
        minCompareRound: this.minCompareRound,
        compareMultiplier: this.compareMultiplier,
        maxPlayers: this.maxPlayers,
        creatorUid: this.creatorUid,
        playingFlag: this.playingFlag,
        numOfRounds: this.getNumOfRounds(),
        potBets: this.potBets,
        actorUid: this.actorUid,
        minBets: this.minBets,
        countDown: this.getCountDown(ACTION_INTERVAL),
        dealerUid: this.dealerUid,
        players: playersJSON
    };
};

Table.prototype.getNumOfRounds = function () {
    let maxBetTimes = 1;
    let minBetTimes = 999999;
    this.players.forEach(function (player) {
        if (player == null) {
            return null;
        }
        if (!player.isAlive()) {
            return null;
        }
        let betTimes = player.getBetTimes();
        if (betTimes > maxBetTimes) {
            maxBetTimes = betTimes;
        }
        if (betTimes < minBetTimes) {
            minBetTimes = betTimes;
        }
    });
    if (maxBetTimes === 0 || minBetTimes === 999999) {
        return 0;
    }
    if (maxBetTimes === minBetTimes) {
        return maxBetTimes + 1;
    } else {
        return maxBetTimes;
    }
};

Table.prototype.toYuanBaoTable = function () {
    let compareMultiplierDesc = (this.compareMultiplier === 2 ? "比牌翻倍" : "比牌不翻倍");
    let description = `封顶${this.maxRounds}轮 必闷 ${this.minSeeCardRound}轮 ${compareMultiplierDesc}`;
    return {
        "tid": this.tid,
        "code": this.code,
        "minStack": this.room.minStack,
        "numOfPlayers": this.getNumOfPlayers(),
        "maxPlayers": this.maxPlayers,
        "description": description,
        "multiplier": this.multiplier,
        "createTime": this.createTime
    };
};

Table.prototype.isVisibleBetween = function (uid1, uid2) {
    return uid1 === uid2 || this.cardVisibilities[uid1 + ' ' + uid2];
};

Table.prototype.addVisibility = function (uid1, uid2) {
    this.cardVisibilities[uid1 + " " + uid2] = 1;
};

Table.prototype.addVisibilityToAll = function (uid) {
    let self = this;
    this.players.forEach(function (player) {
        if (!!player && player.isReady()) {
            self.addVisibility(player.getUid(), uid);
        }
    });
};

Table.prototype.seeCard = function (player) {
    player.seeCard();
    this.broadCast("onZhajinhuaSeenCard", {
        uid: player.getUid()
    });
};

Table.prototype.fold = function (player) {
    player.fold();
    this.broadCast("onZhajinhuaFold", {
        uid: player.getUid(),
    });
    if (this.actorUid === player.getUid() && this.getNumOfAlivePlayers() > 1) {
        this.checkNextRound();
    }
    this.checkGameFinish();
};

Table.prototype.getActorUid = function () {
    return this.actorUid;
};

Table.prototype.compare = function (activePlayer, passivePlayer) {
    let amount = this.minBets * this.compareMultiplier;
    if (!activePlayer.isBlind()) {
        amount = amount * 2;
    }
    if (amount > activePlayer.getNetBalance()) {
        amount = activePlayer.getNetBalance();
    }
    if (amount < 0) {
        logger.error("amount is less than zero. amount=" + amount);
        amount = 0;
    }
    activePlayer.bet(amount);
    this.potBets += amount;
    let result = Poker.getResult(activePlayer.getCards(), passivePlayer.getCards());
    if (result > 0) {
        activePlayer.win(passivePlayer.getUid(), 1);
        passivePlayer.lose(activePlayer.getUid(), -1);
    } else {
        activePlayer.lose(passivePlayer.getUid(), 1);
        passivePlayer.win(activePlayer.getUid(), -1);
    }
    let numOfAlivePlayer = this.getNumOfAlivePlayers();
    if (numOfAlivePlayer === 1) {
        this.addVisibilityToAll(activePlayer.getUid());
        this.addVisibilityToAll(passivePlayer.getUid());
    } else {
        this.addVisibility(activePlayer.getUid(), passivePlayer.getUid());
        this.addVisibility(activePlayer.getUid(), passivePlayer.getUid());
    }
    this.broadCast("onZhajinhuaCompare", {
        "activePlayerUid": activePlayer.getUid(),
        "passivePlayerUid": passivePlayer.getUid(),
        "result": result,
        "amount": amount,
        "balance": activePlayer.getBalance(),
        "betChips": activePlayer.getBetChips(),
        "betTimes": activePlayer.getBetTimes()
    });
    if (this.getNumOfAlivePlayers() > 1) {
        this.checkNextRound();
    }
    this.checkGameFinish();
    return true;
};

Table.prototype.getNumOfAlivePlayers = function () {
    let num = 0;
    this.players.forEach(function (player) {
        if (player != null && player.isAlive()) {
            num = num + 1;
        }
    });
    return num;
};

Table.prototype.bet = function (player, amount) {
    if (amount < this.minBets) {
        logger.error("zhajinhua bet failed. amount is less than minBets. amount=" + amount + " minBets=" + this.minBets);
        return false;
    }
    if (amount !== this.multiplier &&
        amount !== this.multiplier * 2 &&
        amount !== this.multiplier * 3 &&
        amount !== this.multiplier * 4 &&
        amount !== this.multiplier * 5 &&
        amount !== this.multiplier * 6) {
        logger.error("zhajinhua bet failed. amount is invalid. amount=" + amount + " multiplier=" + this.multiplier);
        return false;
    }
    let actualAmount = amount;
    if (!player.isBlind()) {
        actualAmount = actualAmount * 2;
    }
    if (player.getNetBalance() < actualAmount) {
        logger.error("not enough balance. net balance=" + player.getNetBalance() + " actualAmount=" + actualAmount);
        return false;
    }
    player.bet(actualAmount);
    this.potBets += actualAmount;
    this.minBets = amount;
    this.broadCast("onZhajinhuaBet", {
        uid: player.getUid(),
        amount: actualAmount,
        balance: player.getBalance(),
        betChips: player.getBetChips(),
        betTimes: player.getBetTimes()
    });
    if (this.getNumOfAlivePlayers() > 1) {
        this.checkNextRound();
    }
    this.checkGameFinish();
    return true;
};

Table.prototype.setAllIn = function (player, value) {
    player.setAllInValue(value);
};

Table.prototype.checkNextRound = function () {
    let newActorUid = 0;
    let index = -1;
    for (let i = 0; i < this.players.length; i++) {
        if (this.players[i] != null && this.players[i].getUid() === this.actorUid) {
            index = i;
        }
    }
    if (index === -1) {
        logger.error("invalid actorUid");
        return;
    }
    for (let i = (index + 1) % this.players.length; (i % this.players.length) !== index; i++) {
        let player = this.players[i % this.players.length];
        if (player != null && player.isAlive()) {
            newActorUid = player.getUid();
            break;
        }
    }
    if (newActorUid === 0 || newActorUid === this.actorUid) {
        logger.error("invalid new ActorUid");
        return;
    }
    this.actorUid = newActorUid;
    this.actorTime = DateUtil.nowMS();
    this.broadCast("onZhajinhuaNextTurn", {
        "actorUid": this.actorUid,
        "countDown": this.getCountDown(ACTION_INTERVAL),
        "rounds": this.getNumOfRounds(),
        "potBets": this.potBets,
        "minBets": this.minBets
    });
};

Table.prototype.getAlivePlayers = function () {
    let alivePlayers = [];
    for (let i = 0; i < this.players.length; i++) {
        let player = this.players[i];
        if (player != null && player.isAlive()) {
            alivePlayers.push(player);
        }
    }
    return alivePlayers;
};

Table.prototype.calculateResult = function (winner) {
    let resultPlayers = [];
    for(let other of this.players) {
        if (other === null) {
            continue;
        }
        if (!other.isReady()) {
            continue;
        }
        let result = other.getUid() === winner.getUid() ? 1 : -1;
        let award = 0;
        if (result > 0) {
            award = this.potBets - other.getBetChips();
        } else {
            award = -other.getBetChips();
        }
        let cards = other.getCards();
        other.finish(award);
        resultPlayers.push({
            uid: other.getUid(),
            result: result,
            cards: cards,
            award: award,
            balance: other.getBalance(),
            cardType: Poker.calculateHandType(other.cards),
            nickname: other.getNickname()
        });
    }
    return resultPlayers;
};

Table.prototype.checkGameFinish = function () {
    let alivePlayers = this.getAlivePlayers();
    if (alivePlayers.length !== 1) {
        return;
    }
    let winner = alivePlayers[0];
    this.lasWinnerUid = winner.getUid();
    let resultPlayers = this.calculateResult(winner);
    for (let player of this.players) {
        if (player === null) {
            continue;
        }
        let results = [];
        for (let resultPlayer of resultPlayers) {
            let cards = '';
            if (this.isVisibleBetween(player.getUid(), resultPlayer.uid)) {
                cards = resultPlayer.cards;
            }
            results.push({
                uid: resultPlayer.uid,
                result: resultPlayer.result,
                cards: cards,
                award: resultPlayer.award,
                balance: resultPlayer.balance,
                cardType: resultPlayer.cardType,
            })
        }
        player.push("onZhajinhuaFinish", {
            results: results
        });
    }

    let params = {uids: [], amounts:[], params:[]};
    for (let i = 0; i < resultPlayers.length; i++) {
        let resultPlayer = resultPlayers[i];
        params.uids.push(resultPlayer.uid);
        params.amounts.push(resultPlayer.award);
        params.params.push("zhajinhua_finish");
    }
    pomelo.app.rpc.account.tableRemote.changeDiamond(null, params, function (){});

    //add gameRecord and playTimes
    let gameRecord = this.getRecordJSON(resultPlayers);
    pomelo.app.rpc.account.gameRecordRemote.addGameRecord(null, gameRecord, function(response) {
        if (response.resultCode != 0) {
            logger.info(`zhajinhuaYuanBaoTable addGameRecord failed`);
            return;
        }
        logger.info(`zhajinhuaYuanBaoTable addGameRecord ${JSON.stringify(response.gameRecord)}`);
    });

    //add gameResult
    if(!!resultPlayers && resultPlayers.length > 0) {
        let gameResult = this.getResultJSON(resultPlayers);
        pomelo.app.rpc.account.gameResultRemote.addZhajinhuaGameResult(null, gameResult, function(response) {
            if (response.resultCode != 0) {
                logger.info(`zhajinhuaYuanBaoTable addGameRecord failed`);
                return;
            }
            logger.info(`zhajinhuaYuanBaoTable addGameResult ${JSON.stringify(response)}`);
        });
    }

    //reset table
    this.players.forEach(function (player) {
        if (player != null) {
            player.resetInTable();
            player.clearReady();
            player.startGetReadyCountDown(16);
        }
    });
    this.lastReadyPlayerTime = 0;
    this.playingFlag = 0;
    this.dealerUid = 0;
    this.cardVisibilities = {};
    this.actorTime = 0;
    this.actorUid = 0;
    this.potBets = 0;
    this.minBets = 0;
};

Table.prototype.onAddPlayer = function (player) {
    player.push("onInitializeZhajinhuaTable", {table: this.toJSON(),
        myCards: player.getDisplayCards(this)
    });
    this.broadCastWithout("onAddOrUpdateZhajinhuaPlayer", {
        player : player.toJSON()
    }, player.getUid());
};

Table.prototype.getCountDown = function (duration) {
    if (this.actorTime === 0) {
        return 0;
    }
    let interval = duration - DateUtil.getIntervalSec(this.actorTime);
    if (interval <= 0) {
        return 0;
    } else {
        return interval;
    }
};

Table.prototype.getRecordJSON = function (results) {
    let playerScores = [];
    for (let result of results) {
        playerScores.push({
            uid : result.uid,
            nickname: result.nickname,
            score : result.award
        });
    }
    return {
        gameId: this.gid,
        tableId: this.tid,
        time: DateUtil.nowMS(),
        playerScores: playerScores
    }
};

Table.prototype.getResultJSON = function (resultPlayers) {
    let results = [];
    for (let resultPlayer of resultPlayers) {
        let uid = resultPlayer.uid;
        let player = this.findPlayerByUid(uid);
        results.push({
            uid : uid,
            cards : resultPlayer.cards,
            score : resultPlayer.award,
            cardType : resultPlayer.cardType,
            blindFlag : player.blindFlag,
            foldFlag : player.foldFlag,
            compareLoseFlag : player.compareLoseFlag,
            compareOpponentUid : player.compareOpponentUid,
            compareFlag : player.compareFlag,
            betChips : player.betChips,
            betTimes : player.betTimes
        });
    }
    return {
        gid : this.gid,
        tid : this.tid,
        code : this.code,
        multiplier : this.multiplier,
        startTime : this.gameStartTime,
        endTime : DateUtil.nowMS(),
        maxRounds : this.maxRounds,
        minSeeCardRound : this.minSeeCardRound,
        minCompareRound : this.minCompareRound,
        results : results
    }
};

module.exports.create = function (tableCfg) {
    let table = new Table();
    table.tid = tableCfg['tid'];
    table.code = tableCfg['code'];
    table.room = tableCfg['room'];
    table.multiplier = table.room.multiplier;
    table.maxRounds = tableCfg['maxRounds'];
    table.minSeeCardRound = tableCfg['minSeeCardRound'];
    table.minCompareRound = tableCfg['minCompareRound'];
    table.compareMultiplier = tableCfg['compareMultiplier'];
    table.visible = tableCfg['visible'];
    table.creatorUid = tableCfg['creatorUid'];
    table.maxPlayers = tableCfg["maxPlayers"];
    table.createTime = DateUtil.nowMS();
    for (let i = 0; i < table.maxPlayers; i++) {
        table.players.push(null);
    }
    table.dealerUid = 0;
    table.cardVisibilities = {};
    // table.actorTime = 0;
    table.actorUid = 0;
    table.potBets = 0;
    table.minBets = 0;
    table.gid = tableCfg['gid'];
    return table;
};
